//
//  channel.cpp
//  Wi-Fi Party remade-1
//
//  Created by Vieta Ken on 2021/5/29.
//

#include "channel.hpp"

#include <list>
#include <netinet/in.h>
#include <stdio.h>
#include <pthread.h>
#include <map>
#include <queue>
#include <functional>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>

#include "packet.hpp"
#include "app.h"

#include "receiver.hpp"



struct sockaddr_in send_addr={
    #ifdef __APPLE__
    .sin_len=sizeof(struct sockaddr_in),
    #endif
    .sin_family=AF_INET,
    .sin_port=htons(PORT),
    .sin_addr={
        .s_addr=inet_addr(MULTICAST_ADDRESS_STR)
    },
    .sin_zero={0},
};
Channel::Channel(unsigned int channel_id,enum type type,unsigned int resend, int socket,  std::function<bool(Packet*)> handler){
    this->channel_id=channel_id;
    this->type=type;
    this->resend=resend;
    this->socket=socket;
    this->index=0;
    this->handlers=new std::list<std::function<bool(Packet* received_packet)>>;
    this->handlers->push_back(handler);
    if(this->type==channel_reliable){
        std::function<bool(Packet*)> callback=[&](Packet *ack_packet){
            if(ack_packet->content.length==0){
                std::map<int,std::map<unsigned int,bool>*>::iterator find_result=this->receiver->known_hosts->find(ack_packet->from_addr->sin_addr.s_addr);
                if(find_result==this->receiver->known_hosts->end()){
                    //This host is unknown
                    (*(this->receiver->known_hosts))[ack_packet->from_addr->sin_addr.s_addr]=new std::map<unsigned int, bool>;
                }
                (*(*(this->receiver->known_hosts))[ack_packet->from_addr->sin_addr.s_addr])[ack_packet->header->index]=true;
                return true;
            }else{
                return false;
            }
        };
        handlers->push_front(callback);
    }
    //std::list<...> handlers is a list (chain) of handlers
    //When packets come, the Channel will call the handlers one by one until the last one called returned true.
    //The handler registered here should always return true.
    //Returning false values are used by handlers to ensure the packet is send.
    //See Channel::receive_packet(Packet* packet)
}

/*
 * Send data through this channel
 * delay: The time in us to wait between resends
 * only valid when using unreliable channel and resend>1
 */
auto sendPayloadPool=Slab<Packet>(10);
void Channel::send(const void* content, unsigned int length, unsigned int delay){
    Packet *payload=sendPayloadPool.alloc();
    new (payload) Packet(IDENTIFIER, this->channel_id, this->index, (void*)content, length);
    this->index++;
    //send_packet(IDENTIFIER,channel_id,index,this,0);
    switch (this->type) {
        case channel_reliable:
            ensure_sent(payload, this->receiver->known_hosts,[&](){
                    payload->send(this->socket, (struct sockaddr *)&send_addr);
                    });
            break;
        case channel_unreliable:
	    payload->send(this->socket, (struct sockaddr *)&send_addr);
            for(unsigned int i=1;i<this->resend;i++){
                payload->send(this->socket, (struct sockaddr *)&send_addr);
                usleep(delay);
            }
            break;

        default:
            throw "Unknown Channel type";
            break;
    }
    payload->~Packet();
    sendPayloadPool.release(payload);
}
bool all_true(std::map<int,std::map<unsigned int, bool>*> *map,unsigned int index){
    for(std::map<int,std::map<unsigned int, bool>*>::iterator map_iter=map->begin();map_iter!=map->end();map_iter++){
        if((*map_iter->second)[index]==false){//iter->first is key, iter->second is value
            return false;
        }
    }
    return true;
}
void ensure_sent(Packet* packet,std::map<int,std::map<unsigned int, bool>*> *known_hosts,std::function<void()> send_packet){
    send_packet();
    for(std::map<int,std::map<unsigned int,bool>*>::iterator host=known_hosts->begin();host!=known_hosts->end();host++){
        (*host->second)[packet->header->index]=false;
    }
    for(int resend_index=0;resend_index<MAX_RESEND;resend_index++){

        if(all_true(known_hosts,packet->header->index)) break;
        else{
            //send again
            send_packet();
        }
        sleep(MAX_WAIT_ACK);
    }
    //Erase this packet's records in known_hosts
    for(std::map<int,std::map<unsigned int,bool>*>::iterator host=known_hosts->begin();host!=known_hosts->end();host++){
        host->second->erase(packet->header->index);
    }
}


extern Slab<Packet> receivePacketPool;
void Channel::receive_packet(Packet* packet){
    for(std::list<std::function<bool(Packet*)>>::iterator handler=this->handlers->begin();handler!=this->handlers->end();handler++){
        if((*handler)(packet)) break;
    }
    if(this->type==channel_reliable&&packet->content.length!=0){
        packet->ack(this->socket, (struct sockaddr*)&send_addr);
    }
    packet->~Packet();
    receivePacketPool.release(packet);//Freed #1
}
